from playwright.sync_api import Playwright, sync_playwright, expect
import time
import re
import json


def add_js(page):
    js = """
        Object.defineProperties(navigator, {webdriver:{get:()=>undefined}})
    """
    page.add_init_script(js)
    return page


def get_cookies_dict(context_cookies):
    cookies = {}
    for i in context_cookies:
        cookies[i['name']] = i['value']
    return cookies

def get_cookies_str(context_cookies):
    cls = []
    for i in context_cookies:
        cls.append(i['name'] + '=' + i['value'])
    return ';'.join(cls)


def get_tracks(distance):
    v = 0
    t = 0.3
    tracks = []
    current = 0
    mid = distance * 4 / 5
    while current < distance:
        if current < mid:
            a = 2
        else:
            a = -3
        v0 = v
        s = v0 * t + 0.5 * a * (t ** 2)
        current += s
        tracks.append(round(s))
        v = v0 + a * t
    return tracks


class UrlListener:

    def __init__(self, page):
        self.page = page
        self.url_pattern_ls = []
        self.url_response_dict = {}
        self.match_funcs = []

    def listen(self, urls):
        for url in urls:
            self.url_pattern_ls.append(url)
            self.page.on('response', self.on_response)
            print('监听', url)

    def on_response(self, response):
        for url_pattern in self.url_pattern_ls:
            if re.search(url_pattern, response.url) :
                print('发现监听的url:', response.url)
                if self.match_funcs:
                    for func in self.match_funcs:
                        func(self, response)
                else:
                    self.url_response_dict[url_pattern] = response
    
    def add_match_fun(self, fun):
        self.match_funcs .append(fun)


class Page:

    def __init__(self, context):
        self.context = context
        self.page = add_js(self.context.new_page())
        self.url_listener = UrlListener(self.page)

    def goto(self,
             url,
             wait_text=None,
             wait_xpath=None,
             wait_time=None,
             listen_urls = None):
        '''
        访问一个网址
        url: 网页链接
        wait_text: 加载网页直到wait_text文本出现
        wait_xpath: 加载网页直到wait_xpath匹配的元素出现
        wait_time: 加载网页wait_time秒
        listen_urls: 要监听的ajax请求的url列表
        '''
        if listen_urls:
            self.url_listener.listen(listen_urls)
        self.page.goto(url)
        if wait_time:
            self.last_busy_time = time.time()
            while time.time() - self.last_busy_time < wait_time:
                self.page.wait_for_timeout(100)
        if wait_text:
            self.page.locator('text={}'.format(wait_text))
        if wait_xpath:
            self.page.locator(wait_xpath)
    
    def listen(self, listen_urls):
        self.url_listener.listen(listen_urls)

    def get_cookies(self):
        return get_cookies_dict(self.context.cookies())
    
    def get_cookies_str(self):
        return get_cookies_str(self.context.cookies())

    def screenshot(self, save_path):
        if save_path:
            return self.page.screenshot(path=save_path, full_page=True)
        else:
            return self.page.screenshot(full_page=True)

    def get_content(self):
        return self.page.content()

    def click(self, locator=None, text=None):
        if text:
            self.page.locator('text={}'.format(text)).click()
        elif locator:
            self.page.locator(locator).click()

    def drag(self, locator=None, distance=0):
        '''
        滑动滑块
        '''
        if locator:
            src_elem = self.page.locator(locator)
        tracks_x = get_tracks(distance)
        tracks_length = len(tracks_x)
        box = src_elem.bounding_box(timeout=60000)
        self.page.mouse.move(box["x"] + box["width"] / 2, box["y"] + box["height"] / 2)
        self.page.mouse.down()
        new_x = box["x"] + box["width"] / 2
        for i in range(tracks_length):
            new_x += tracks_x[i]
            self.page.mouse.move(new_x, box["y"] + box["height"] / 2)
        time.sleep(1.0)
        self.page.mouse.up()

    def input(self, locator=None, text=''):
        '''
        填表
        '''
        if locator:
            self.page.locator(locator).fill(text)

    def get_img(self, locator=None, save_path=None):
        '''
        获取网页中的图片
        '''
        if locator:
            if save_path:
                return self.page.locator(locator).screenshot(path=save_path)
            else:
                return self.page.locator(locator).screenshot()

    def add_cookie_ls(self, cookie_ls):
        '''
        添加cookies
        '''
        self.context.add_cookies(cookie_ls)

    def save_state(self, save_path):
        '''
        保存访问状态，类似于保存cookies
        '''
        self.context.storage_state(path=save_path)

    def clear_cookies(self):
        self.context.clear_cookies()
    
    def find(self, locator=None):
        try:
            self.page.locator(locator)
        except:
            return False
        return True
    
    def add_state_cookie_ls(self, state_json_file):
        with open(state_json_file, 'r', encoding='utf-8') as fp:
            state_json = json.load(fp)
        self.add_cookie_ls(state_json['cookies'])
    
    def press(self, keyword):
        self.page.keyboard.press(key=keyword)
    
    def wheel(self,x, y):
        self.page.mouse.wheel(x, y)


class Web:
    '''
    实例化后记得使用close函数关闭
    '''

    def __init__(self, proxy:dict = None, headless=False, timezone_id='Asia/Shanghai'):
        # proxy : {'server': 'socks5://192.168.1.105:37891'}
        self.playwright = sync_playwright().__enter__()
        if not proxy:
            self.browser = self.playwright.firefox.launch(headless=headless)
        else:
            self.browser = self.playwright.firefox.launch(
                headless=headless,
                proxy=proxy
            )
        self.timezone_id = timezone_id
        self.context = self.browser.new_context(timezone_id=timezone_id)
        # page = self.context.new_page()
        self.page_dict = {}
    def create_page(self, page_name) -> Page:
        self.page_dict[page_name] = Page(self.context)
        return self.page_dict[page_name]

    def screenshot(self, page_name, save_path=None):
        return self.page_dict[page_name].screenshot(save_path=save_path)

    def close(self):
        self.context.close()
        self.browser.close()

    def __getitem__(self, key):
        return self.page_dict[key]
    
    def load_state(self, state):
        self.context = self.browser.new_context(timezone_id=self.timezone_id,storage_state=state)